package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.api.dto.leanring.LearningRecordFormDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.tianji.learning.utils.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;

@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final ILearningLessonService lessonService;

    private final CourseClient courseClient;

    private final LearningRecordDelayTaskHandler taskHandler;

    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        // 2.查询课表
        LearningLesson lesson = lessonService.queryByUserAndCourseId(userId, courseId);
        // 3.查询学习记录
        // select * from xx where lessonId = #{lessonId}
        List<LearningRecord> records = lambdaQuery()
                .eq(LearningRecord::getLessonId, lesson.getId()).list();
        // 4.封装结果
        LearningLessonDTO dto = new LearningLessonDTO();
        dto.setId(lesson.getId());
        dto.setLatestSectionId(lesson.getLatestSectionId());
        dto.setRecords(BeanUtils.copyList(records, LearningRecordDTO.class));
        return dto;
    }

    /**
     * 提交学习记录
     * @param learningRecordFormDTO
     */
    @Override
    public void addLearningRecord(LearningRecordFormDTO learningRecordFormDTO) {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        // 2.处理学习记录
        boolean finished = false;
        if (learningRecordFormDTO.getSectionType() == SectionType.VIDEO.getValue()) {
            // 2.1 处理视频
            finished = handleVideoRecord(userId, learningRecordFormDTO);
        } else {
            // 2.2 处理考试
            finished = handleExamRecord(userId, learningRecordFormDTO);
        }
        if (!finished) {
            // 没有新学完的小节，无需更新课表中的学习进度
            return;
        }
        // 3.处理课程数据
        handleLearningLessonsChanges(learningRecordFormDTO, finished);
    }

    /**
     * 处理课程数据
     * @param learningRecordFormDTO
     * @param finished
     */
    private void handleLearningLessonsChanges(LearningRecordFormDTO learningRecordFormDTO, boolean finished) {
        // 1.查询课表
        LearningLesson lesson = lessonService.getById(learningRecordFormDTO.getLessonId());
        if (lesson == null) {
            throw new BizIllegalException("课程不存在，无法更新数据！");
        }
        // 2.判断是否有新的完成小节
        boolean allLearned = false;
        if (finished) {
            // 3.如果有新完成的小姐，则需要查询课程数据
            CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
            if (cInfo == null) {
                throw new BizIllegalException("课程不存在，无法更新数据");
            }
            // 4.比较课程是否全部学完：已学习小节 >= 课程小节
            allLearned = lesson.getLearnedSections() + 1 >= cInfo.getSectionNum();
        }
        // 5.更新课表
        lessonService.lambdaUpdate()
                // 如果课程的已学习小节数为0，则将课程状态设置为学习中
                .set(lesson.getLearnedSections() == 0, LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                // 如果课程已全部学完，则将课程状态设置为已完成
                .set(allLearned, LearningLesson::getStatus, LessonStatus.FINISHED.getValue())
                // 如果课程已全部学完，则将课程的最新学习时间设置为当前时间
                .set(allLearned, LearningLesson::getLatestLearnTime, LocalDateTime.now())
                // 如果课程已完成，则将已学习小节数加1
                .setSql(finished, "learned_sections = learned_sections + 1")
                // 根据课程ID更新课程信息
                .eq(LearningLesson::getId, lesson.getId())
                // 执行更新操作
                .update();
    }

    /**
     * 处理考试
     * @param userId
     * @param learningRecordFormDTO
     * @return
     */
    private boolean handleExamRecord(Long userId, LearningRecordFormDTO learningRecordFormDTO) {
        // 1.转换DTO为PO
        LearningRecord learningRecord = BeanUtils.copyBean(learningRecordFormDTO, LearningRecord.class);
        // 2.填充数据
        learningRecord.setUserId(userId);
        learningRecord.setFinished(true);
        learningRecord.setFinishTime(learningRecordFormDTO.getCommitTime());
        // 3.写入数据库
        boolean success = save(learningRecord);
        if (!success) {
            throw new DbException("新增考试记录失败！");
        }
        return true;
    }

    /**
     * 处理视频
     * @param userId
     * @param learningRecordFormDTO
     * @return
     */
    private boolean handleVideoRecord(Long userId, LearningRecordFormDTO learningRecordFormDTO) {
        // 1.查询旧的学习记录
        // 1.1 查询缓存
        LearningRecord old = taskHandler.readRecordCache(learningRecordFormDTO.getLessonId(), learningRecordFormDTO.getSectionId());
        // 1.2 如果未命中，查询数据库
        if (null == old) {
            old = lambdaQuery()
                    // 设置查询条件为 LearningRecord 的 lessonId 字段等于 learningRecordFormDTO 中的 lessonId 值
                    .eq(LearningRecord::getLessonId, learningRecordFormDTO.getLessonId())
                    // 设置查询条件为 LearningRecord 的 sectionId 字段等于 learningRecordFormDTO 中的 sectionId 值
                    .eq(LearningRecord::getSectionId, learningRecordFormDTO.getSectionId())
                    // 执行查询操作，并返回第一条记录
                    .one();
        }
        // 1.3 写入缓存
        taskHandler.writeRecordCache(old);
        // 2.判断是否存在
        if (old == null) {
            // 3.不存在，则新增
            // 3.1 转换PO
            LearningRecord learningRecord = BeanUtils.copyBean(learningRecordFormDTO, LearningRecord.class);
            // 3.2 填充数据
            learningRecord.setUserId(userId);
            // 3.3 写入数据库
            boolean success = save(learningRecord);
            if (!success) {
                throw new DbException("新增学习记录失败！");
            }
            return false;
        }
        // 4.存在，则更新
        // 4.1 判断是否是第一次完成
        boolean finished = !old.getFinished() && learningRecordFormDTO.getMoment() * 2 >= learningRecordFormDTO.getDuration();
        if (!finished) {
            LearningRecord learningRecord = new LearningRecord();
            learningRecord.setLessonId(learningRecordFormDTO.getLessonId());
            learningRecord.setSectionId(learningRecordFormDTO.getSectionId());
            learningRecord.setMoment(learningRecordFormDTO.getMoment());
            learningRecord.setId(old.getId());
            learningRecord.setFinished(old.getFinished());
            taskHandler.addLearningRecordTask(learningRecord);
            return false;
        }
        // 4.2 更新数据
        // 使用 lambdaUpdate() 方法构建更新条件
        boolean success = lambdaUpdate()
                // 设置 LearningRecord 的 moment 字段为 learningRecordFormDTO 中的 moment 值
                .set(LearningRecord::getMoment, learningRecordFormDTO.getMoment())
                // 如果 finished 为 true，则设置 LearningRecord 的 finished 字段为 true
                .set(finished, LearningRecord::getFinished, true)
                // 如果 finished 为 true，则设置 LearningRecord 的 finishTime 字段为 learningRecordFormDTO 中的 commitTime 值
                .set(finished, LearningRecord::getFinishTime, learningRecordFormDTO.getCommitTime())
                // 设置更新条件为 LearningRecord 的 id 字段等于 old.getId()
                .eq(LearningRecord::getId, old.getId())
                // 执行更新操作，并将结果赋值给 success 变量
                .update();
        if (!success) {
            throw new DbException("更新学习记录失败");
        }
        // 4.3 清理缓存
        taskHandler.cleanRecordCache(learningRecordFormDTO.getLessonId(), learningRecordFormDTO.getSectionId());
        return finished;
    }
}